home *** CD-ROM | disk | FTP | other *** search
/ Languguage OS 2 / Languguage OS II Version 10-94 (Knowledge Media)(1994).ISO / gnu / libg_261.zip / libg_261 / libg++ / etc / graph / graph.cc < prev    next >
C/C++ Source or Header  |  1994-05-13  |  24KB  |  698 lines

  1. // graph.cc --- graph reads data and writes out a plot file.
  2.  
  3. const char *copyright_notice = "\
  4. Copyright (C) 1989 Free Software Foundation \n\
  5.  \n\
  6. This file is part of GNU CC. \n\
  7.  \n\
  8. GNU CC is distributed in the hope that it will be useful, \n\
  9. but WITHOUT ANY WARRANTY.  No author or distributor \n\
  10. accepts responsibility to anyone for the consequences of using it \n\
  11. or for whether it serves any particular purpose or works at all, \n\
  12. unless he says so in writing.  Refer to the GNU CC General Public \n\
  13. License for full details. \n\
  14.  \n\
  15. Everyone is granted permission to copy, modify and redistribute \n\
  16. GNU CC, but only under the conditions described in the \n\
  17. GNU CC General Public License.   A copy of this license is \n\
  18. supposed to have been given to you along with GNU CC so you \n\
  19. can know your rights and responsibilities.  It should be in a \n\
  20. file named COPYING.  Among other things, the copyright notice \n\
  21. and this notice must be preserved on all copies. \n\
  22. ";
  23.  
  24. #include "read_data.h"
  25. #include "eGetOpt.h"
  26. #include "ePlotFile.h"
  27. #include "tick_intrvl.h"
  28. #include <builtin.h>
  29. #include <strstream.h>
  30.  
  31. const char *usage_message = "\
  32.   [options...]\n\
  33. \n\
  34.     Option:                         Description:\n\
  35.     -C                              print copyright notice\n\
  36.     -D                              binary double precision data\n\
  37.     -E                              use extended plot file format\n\
  38.     -H CHARACTER_HEIGHT             fractional height of characters\n\
  39.     -I                              binary integer data\n\
  40.     -K                              switch symbol for each new line\n\
  41.     -L                              switch line style for each new line\n\
  42.     -M [x|y] MARGIN                 margin between data and edges of box\n\
  43.     -N TICKS                        number of tick marks on each axis\n\
  44.     -P SIZE                         plot file coordinate range\n\
  45.     -S SYMBOL_NUMBER SYMBOL_SIZE    draw symbols at each point\n\
  46.     -T TICK_SIZE                    fractional size of tick marks\n\
  47.     -W CHARACTER_WIDTH              fractional width of characters\n\
  48.     -X X_LABEL                      label printed below the x axis\n\
  49.     -Y Y_LABEL                      label printed right of the y axis\n\
  50.     -a STEP_SIZE LOWER_LIMIT        generate abcissa, read only y values\n\
  51.     -b                              break lines whenever x decreases\n\
  52.     -c POINT_LABEL                  default label printed at each point\n\
  53.     -d                              print debugging information\n\
  54.     -g GRID_STYLE                   draw a grid in the plot\n\
  55.     -h HEIGHT                       fractional height of the plot\n\
  56.     -l TOP_LABEL                    label printed above the plot\n\
  57.     -m LINE_MODE                    solid and dashed lines\n\
  58.     -r RIGHT                        move plot right by fractional ammount\n\
  59.     -s                              save the screen - do not erase\n\
  60.     -t                              transpose x ans y axes\n\
  61.     -u UP                           move plot up by fractional ammount\n\
  62.     -w WIDTH                        fractional width of the plot\n\
  63.     -x [lTB] LOWER_LIMIT UPPER_LIMIT log scale, axis limits\n\
  64.     -y [lLR] LOWER_LIMIT UPPER_LIMIT log scale, axis limits\n\
  65.     -z                              do not read data from standard input\n\
  66. ";
  67.  
  68. // Here are the command line option data and flags:
  69.  
  70. String default_label;        // default label at each point.
  71. String top_label;        // label above the plot.
  72. String x_label;            // x axis label
  73. String y_label;            // y axis label
  74. data_type input_data = ASCII;    // the type of data to be read in.
  75. double char_height = .03;    // fractional height of printed characters
  76. double char_width = .02;    // fractional width of printed characters
  77. double height = .8;        // fraction height of the plot.
  78. double lower_limit = 0.;    // lower limit in x for generated values
  79. double no_of_ticks = 5.;    // number of tick marks on the axes.
  80. double right = .1;        // the fractional margin on the right side.
  81. double size_of_ticks = .01;    // fractional size of the tick marks.
  82. double spacing = 1.;        // stepsize for equally spaced generated values
  83. double symbol_size = .01;    // index of symbol drawn at each point.
  84. double up = .1;            // the fractional margin above the plot.
  85. double width = .8;        // fraction width of the plot.
  86. double x_lower_limit = DBL_MAX;    // DBL_MAX means get it from the data
  87. double x_margin = 0.0;        // fractional margin between data and box
  88. double x_upper_limit = DBL_MAX;    // DBL_MAX means get it from the data
  89. double y_lower_limit = DBL_MAX;    // DBL_MAX means get it from the data
  90. double y_margin = 0.05;        // fractional margin between data and box
  91. double y_upper_limit = DBL_MAX;    // DBL_MAX means get it from the data
  92. int abcissa_flag = 0;        // nonzero means generate x axiz values
  93. int break_flag = 0;        // break the line whenever x decreases.
  94. int debug_flag = 0;        // verbose debugging output.
  95. int extended_plot_format = 0;    // nonzero means use adjusted labels.
  96. int grid_style = 1;        // style of box and or axes.
  97. int line_style = 0;        // the type of line drawn to connect points.
  98. int no_standard_input = 0;    // nonzero means do not read from standard input
  99. int plot_size = 4096;        // upper limit of plot file coordinates
  100. int save_screen_flag = 0;    // nonzero means do not erase before plotting.
  101. int switch_style = 0;        // switch line style for each new curve
  102. int switch_symbols = 0;        // switch symbols when starting each new curve
  103. int symbol_number = -1;        // index of symbol drawn at each point.
  104. int transpose_axes_flag = 0;    // nonzero means interchange x and y axes.
  105. int x_label_on_top = 0;        // nonzero means label tick marks on right side
  106. int x_log_scale = 0;        // the x axis is log scale
  107. int y_label_on_right = 0;    // nonzero means label tick marks on top
  108. int y_log_scale = 0;        // the y axis is log scale
  109.  
  110. // the names of line styles recognized in the unix plot file convention.
  111. char *line_style_name[] =
  112. {
  113.   "solid",
  114.   "longdashed",
  115.   "dotted",
  116.   "disconnected",
  117.   "dotdashed",
  118.   "shortdashed"
  119.   };
  120. const int no_of_line_styles = sizeof(line_style_name)/sizeof(line_style_name[0]);
  121.  
  122. // This is all the data describing how to draw the symbols.
  123.                                 
  124. typedef enum op {END, CONT, MOVE, CIRCLE}; // a graphic operation
  125.  
  126. struct coord            // a component coordintate within a symbol
  127. {
  128.   double x, y;            // fractional coordinates
  129.   op operation;            // the type of graphic
  130. };
  131.  
  132. struct coord symbol[][10] =    // set of symbols
  133. {
  134.   {                // plus sign
  135.     { -.5,  .0, MOVE},
  136.     {  .5,  .0, CONT},
  137.     {  .0, -.5, MOVE},
  138.     {  .0,  .5, CONT},
  139.     {  .0,  .0, END}
  140.   }, {                // cross
  141.     { -.5, -.5, MOVE},
  142.     {  .5,  .5, CONT},
  143.     {  .5, -.5, MOVE},
  144.     { -.5,  .5, CONT},
  145.     {  .0,  .0, END}
  146.   }, {                // diamond
  147.     { -.5,  .0, MOVE},
  148.     {  .0,  .5, CONT},
  149.     {  .5,  .0, CONT},
  150.     {  .0, -.5, CONT},
  151.     { -.5,  .0, CONT},
  152.     {  .0,  .0, END}
  153.   }, {                // square
  154.     { -.5, -.5, MOVE},
  155.     { -.5,  .5, CONT},
  156.     {  .5,  .5, CONT},
  157.     {  .5, -.5, CONT},
  158.     { -.5, -.5, CONT},
  159.     {  .0,  .0, END}
  160.   }, {                // triangle
  161.     { -.5, -.5, MOVE},
  162.     {  .0,  .86603, CONT},
  163.     {  .5, -.5, CONT},
  164.     { -.5, -.5, CONT},
  165.     {  .0,  .0, END}
  166.   }, {                // circle
  167.     {  .5,  .0, CIRCLE},
  168.     {  .0,  .0, END}
  169.   }, {                // circle with a line through it
  170.     {  .5,  .0, CIRCLE},
  171.     {  .0, -.5, MOVE},
  172.     {  .0,  .5, CONT},
  173.     {  .0,  .0, END}
  174.   }                // add more symbols here...
  175. };
  176. const int no_of_symbols = sizeof(symbol) / sizeof(symbol[0]);
  177.  
  178. // Here are the functions for transforming and clipping.
  179.  
  180. inline int px (double t)    // transform fractional x to plot x
  181. {
  182.   return (int) (plot_size * (width * t + right)); // should we round rather than
  183.                           // truncate here?
  184. }
  185.  
  186. inline int py (double t)    // transform fractional x to plot x
  187. {
  188.   return (int) (plot_size * (height * t + up));
  189. }
  190.  
  191. inline double clip (double t)
  192. {
  193.   double tmp = ( t > 0.0) ? t : 0.0;
  194.   return (tmp < 1.0) ? tmp : 1.0;
  195. }
  196.  
  197. // uppper and lower bounds on the data
  198. double xmin = DBL_MAX, ymin = DBL_MAX, xmax = -DBL_MAX, ymax = -DBL_MAX;
  199. double log_xmin, log_ymin, log_xmax, log_ymax;
  200.  
  201. inline double fx (double t)    // transform data x to fractional x
  202. {
  203.   return x_log_scale ?
  204.     (log (t) - log_xmin) / (log_xmax - log_xmin) :
  205.     (t - xmin) / (xmax - xmin);
  206. }
  207.  
  208. inline double fy (double t)    // transform data y to fractional y
  209. {
  210.   return y_log_scale ?
  211.     (log (t) - log_ymin) / (log_ymax - log_ymin) :
  212.     (t - ymin) / (ymax - ymin);
  213. }
  214.  
  215. inline int in_box (double x, double y) // return 1 if point is inside box
  216. {
  217.   return (x >= xmin) && (x <= xmax) && (y >= ymin) && (y <= ymax);
  218. }
  219.  
  220. int
  221. main (int argc, char **argv)
  222. {
  223.   eGetOpt getopt
  224.     (argc, argv,
  225.      "CDEH::IJKLM::N::P::ST::W::X::Y::a::bc::dg::h::l::m::r::stu::vw::x::y::z");
  226.   int option_char;
  227.   int error_occurred = 0;    // non zero for a bad command line option
  228.   
  229.   while (EOF != (option_char = getopt ()))
  230.     switch (option_char)
  231.       {
  232.       case 'C':
  233.     cerr << copyright_notice; break;
  234.       case 'D':    input_data = DOUBLE; break;
  235.       case 'E':    extended_plot_format++; break;
  236.       case 'H': getopt.next_arg (char_height); break;
  237.       case 'I':    input_data = INT; break;
  238.       case 'K':    switch_symbols++; break;
  239.       case 'L':    switch_style++; break;
  240.       case 'M':
  241.     if ('x' == getopt.first_char())
  242.       {getopt.optind++; getopt.next_arg (x_margin); break;}
  243.     if ('y' == getopt.first_char())
  244.       {getopt.optind++; getopt.next_arg (y_margin);} break;
  245.       case 'N':    getopt.next_arg (no_of_ticks); break;
  246.       case 'P':    getopt.next_arg (plot_size); break;
  247.       case 'S':    getopt.next_arg (symbol_number); 
  248.     getopt.next_arg (symbol_size); break;
  249.       case 'T': getopt.next_arg (size_of_ticks); break;
  250.       case 'W': getopt.next_arg (char_width); break;
  251.       case 'X': getopt.next_arg (x_label); break;
  252.       case 'Y': getopt.next_arg (y_label); break;
  253.       case 'a':    abcissa_flag++; getopt.next_arg (spacing);
  254.     getopt.next_arg (lower_limit); break;
  255.       case 'b':    break_flag++; break;
  256.       case 'c':    getopt.next_arg (default_label); break;
  257.       case 'd':    debug_flag++; break;
  258.       case 'g': getopt.next_arg (grid_style); break;
  259.       case 'h': getopt.next_arg (height); break;
  260.       case 'l': getopt.next_arg (top_label); break;
  261.       case 'm': getopt.next_arg (line_style); break;
  262.       case 'r': getopt.next_arg (right); break;
  263.       case 's': save_screen_flag++; break;
  264.       case 't': transpose_axes_flag++; break;
  265.       case 'u': getopt.next_arg (up); break;
  266.       case 'v': cerr << "graph version 0.\n"; break;
  267.       case 'w': getopt.next_arg (width); break;
  268.       case 'x':
  269.     while  (isalpha (getopt.first_char()))
  270.       {
  271.         switch (getopt.first_char())
  272.           {
  273.           case 'T': x_label_on_top++; getopt.optind++; break;
  274.               case 'B': x_label_on_top=0; getopt.optind++; break;
  275.               case 'l': x_log_scale++; getopt.optind++; break;
  276.               }
  277.       }
  278.     getopt.next_arg (x_lower_limit); getopt.next_arg (x_upper_limit); break;
  279.       case 'y':
  280.     while  (isalpha (getopt.first_char()))
  281.       {
  282.         switch (getopt.first_char())
  283.           {
  284.           case 'L': y_label_on_right=0; getopt.optind++; break;
  285.               case 'R': y_label_on_right++; getopt.optind++; break;
  286.               case 'l': y_log_scale++; getopt.optind++; break;
  287.               }
  288.       }
  289.     getopt.next_arg (y_lower_limit); getopt.next_arg (y_upper_limit); break;
  290.       case 'z': no_standard_input++; break;
  291.       case '?': error_occurred++;
  292.       }
  293.   if (error_occurred) {
  294.     cerr << "usage" sp argv[0] sp usage_message;
  295.     exit (-1);    
  296.   }
  297.                 // Complain if the plot does not fits on page
  298.   if (up < 0.) cerr <<
  299.     "Warning: the plot may extend below the bottom of the page.\n";
  300.   if (up + height > 1.) cerr <<
  301.     "Warning: the plot may extend above the top of the page.\n";
  302.   if (right < 0.) cerr <<
  303.     "Warning: the plot may extend beyond the left edge of the page.\n";
  304.   if (right + width > 1.) cerr <<
  305.     "Warning: the plot may extend beyond the right edge of the page.\n";
  306.  
  307.                    // now we start reading in all the data.
  308.   pointXPlex point;        // all the data is held in an array of points
  309.   
  310.                 // read data from standard input.
  311.   if (! no_standard_input)
  312.       read_data (cin, "(stdard input)", point, abcissa_flag,
  313.          lower_limit, spacing, symbol_number,
  314.          input_data, switch_symbols);
  315.   
  316.                 // read data files specified on command line.
  317.   int i;
  318.   for (i=getopt.optind; i<getopt.nargc; i++)
  319.     {
  320.       char *filename = getopt.nargv[i];
  321.       ifstream input_file(filename);
  322.       if (cin.readable ())
  323.       read_data (cin, filename, point, abcissa_flag,
  324.              lower_limit, spacing, symbol_number,
  325.              input_data, switch_symbols);
  326.     }
  327.                    // The data is read in. Look for limits.
  328.   ePlotFile plot_file (fileno (stdout));
  329.   if (!save_screen_flag)
  330.     plot_file.erase ();
  331.  
  332.   if (point.length () <= 0)
  333.     {                // Complain if there is no data.
  334.       cerr << argv[0] << ": Warning, no data found in input files.\n";
  335.       xmin = 0;
  336.       ymin = 1;
  337.       xmax = 0;
  338.       ymax = 1;
  339.     }
  340.   else
  341.     {                // if there is data...
  342.       if (debug_flag)
  343.     for (i = point.low (); i < point.fence (); point.next (i))
  344.       {
  345.         cerr << point[i].x sp point[i].y;
  346.         if (point[i].label)
  347.           cerr sp point[i].label;
  348.         cerr nl;
  349.       };
  350.   
  351.       if (transpose_axes_flag)
  352.     {
  353.       String tmp;
  354.       tmp = y_label;
  355.       y_label = x_label;
  356.       x_label = tmp;
  357.       double t;
  358.       for (i = point.low (); i < point.fence (); point.next (i))
  359.         {
  360.           t = point[i].y;
  361.           point[i].y = point[i].x;
  362.           point[i].x = point[i].y;
  363.         }
  364.     }
  365.       // find the upper and lower limits
  366.       // of the x any y coordinates.
  367.       for (i = point.low (); i < point.fence (); point.next (i))
  368.     {
  369.       if (xmin > point[i].x) xmin = point[i].x;
  370.       if (ymin > point[i].y) ymin = point[i].y;
  371.       if (xmax < point[i].x) xmax = point[i].x;
  372.       if (ymax < point[i].y) ymax = point[i].y;
  373.     }
  374.       // add margins beteen edges of the data and box if range is nonzero and
  375.       // the scale is not logarithmic.
  376.       if (!y_log_scale)
  377.     {
  378.       double tmp = (ymax - ymin);
  379.       ymax += y_margin * tmp;
  380.       ymin -= y_margin * tmp;
  381.     }
  382.       if (!x_log_scale)
  383.     {
  384.       double tmp = (xmax - xmin);
  385.       xmax += x_margin * tmp;
  386.       xmin -= x_margin * tmp;
  387.     }
  388.     }
  389.   
  390.   // use limits specified on the command line if present.
  391.   if (x_lower_limit != DBL_MAX) xmin = x_lower_limit;
  392.   if (y_lower_limit != DBL_MAX) ymin = y_lower_limit;
  393.   if (x_upper_limit != DBL_MAX) xmax = x_upper_limit;
  394.   if (y_upper_limit != DBL_MAX) ymax = y_upper_limit;
  395.   
  396.   // make sure that 0 is not in range if we are using a log scale.
  397.   if (   (x_log_scale
  398.       && (xmin <= 0.)
  399.       && (xmax >= 0.))
  400.       || (y_log_scale
  401.       && (ymin <= 0.)
  402.       && (ymax >= 0.)))
  403.     {
  404.       cerr << "the lower bound on x is" sp xmin nl;
  405.       cerr << "the upper bound on x is" sp xmax nl;
  406.       cerr << "the lower bound on y is" sp ymin nl;
  407.       cerr << "the upper bound on y is" sp ymax nl;
  408.       cerr << "Zero cannot lie between an upper and lower bound" nl
  409.     "if you use a log scale." nl;
  410.       exit (-1);
  411.     }
  412.   if (x_log_scale)
  413.     {
  414.       log_xmin = log (xmin);
  415.       log_xmax = log (xmax);
  416.     }
  417.   if (y_log_scale)
  418.     {
  419.       log_ymin = log (ymin);
  420.       log_ymax = log (ymax);
  421.     }
  422.                // We have the limits, Now plot.
  423.   plot_file.space (0, 0, plot_size, plot_size);
  424.   // draw a box around the data.
  425.   plot_file.linemod ("solid");
  426.   if (grid_style)
  427.     plot_file.box (px (0.), py (0.), px (1.), py (1.));
  428.   
  429.   char tick_label[32];    // tick lables are less than 16 digits long.
  430. #ifdef _OLD_STREAMS
  431. #define SET_TICK_LABEL(x) strcpy(tick_label, dtoa(x))
  432. #else
  433.   ostrstream tick_stream(tick_label, 32);
  434. #define SET_TICK_LABEL(x) tick_stream.seekp(0), tick_stream << (x) << ends
  435. #endif
  436.   // draw x tick marks.
  437.   if (grid_style)
  438.     {
  439.       // draw labels and ticks on x axis.
  440.       double x_tick, x_tick_value;
  441.  
  442.       if (x_log_scale)
  443.     {
  444.       double logmin = log10 (xmin);
  445.       double logmax = log10 (xmax);
  446.       x_tick = tick_interval (no_of_ticks, logmin, logmax);
  447.       x_tick_value =
  448.         pow (10., x_tick * (x_tick > 0.
  449.                 ? ceil (logmin * A_HAIR_MORE / x_tick)
  450.                 : floor  (logmin * A_HAIR_MORE / x_tick)));
  451.     }
  452.       else
  453.     {
  454.       x_tick = tick_interval (no_of_ticks, xmin, xmax);
  455.       x_tick_value = x_tick
  456.         * (x_tick > 0. ? ceil (xmin * A_HAIR_MORE / x_tick)
  457.            : floor (xmin * A_HAIR_MORE / x_tick));
  458.     }
  459.       while (x_tick_value <= xmax * A_HAIR_MORE)
  460.     {            // tick marks on axes.
  461.       plot_file.line
  462.         (px (fx (x_tick_value)), py (0.),
  463.          px (fx (x_tick_value)), py (-1. * size_of_ticks));
  464.       plot_file.line
  465.         (px (fx (x_tick_value)), py (1.),
  466.          px (fx (x_tick_value)), py (1. + size_of_ticks));
  467.       SET_TICK_LABEL(x_tick_value);
  468.       plot_file.move
  469.         (px (fx (x_tick_value) - (extended_plot_format ? 0 :
  470.                       .5 * char_width * strlen (tick_label))),
  471.          py ((x_label_on_top ? 1. : (extended_plot_format ? 0 : -1.
  472.                      * char_height))
  473.          + (x_label_on_top ? 1. : -1.) * ((size_of_ticks < 0.) ? 0. : size_of_ticks)));
  474.       if (extended_plot_format)
  475.         plot_file.alabel (CENTER_JUSTIFY, x_label_on_top ? BOTTOM_FLUSH
  476.                   : TOP_FLUSH, tick_label);
  477.       else
  478.         plot_file.label (tick_label);
  479.       if (grid_style == 2)
  480.         {            // grid across box.
  481.           plot_file.linemod ("shortdashed");
  482.           plot_file.line
  483.         (px (fx (x_tick_value)), py (0.),
  484.          px (fx (x_tick_value)), py (1.));
  485.           plot_file.linemod ("solid");
  486.         }
  487.       if ((.5 < fx (x_log_scale ? pow (10., log10 (x_tick_value) + x_tick)
  488.             : x_tick_value + x_tick))
  489.           && x_label.length ())
  490.         {            // put the label between tick marks
  491.           plot_file.move
  492.         (px (fx ((x_log_scale ? pow (10., log10 (x_tick_value)+x_tick/2.)
  493.               : x_tick_value + x_tick / 2.)
  494.              - (extended_plot_format ? 0 : 
  495.                 .5 * char_width * x_label.length ()))),
  496.          py ((x_label_on_top ? 1. : (extended_plot_format ? 0 : -1.
  497.                          * char_height)
  498.               + (x_label_on_top ? 1. : -1.) * ((size_of_ticks < 0.) ? 0. : size_of_ticks))));
  499.           if (extended_plot_format)
  500.         plot_file.alabel (CENTER_JUSTIFY, x_label_on_top ? BOTTOM_FLUSH
  501.                   : TOP_FLUSH, x_label);
  502.           else
  503.         plot_file.label (x_label);
  504.           x_label = "";
  505.         }
  506.       if (x_log_scale)
  507.         x_tick_value *= pow (10., x_tick);
  508.       else
  509.       x_tick_value += x_tick;
  510.       if (!x_log_scale && fabs (x_tick_value / x_tick) < 1e-7)
  511.         x_tick_value = 0.;
  512.     }
  513.                    // draw labels and ticks on y axis.
  514.       double y_tick = tick_interval (no_of_ticks, ymin, ymax);
  515.       double y_tick_value = y_tick
  516.     * (y_tick > 0. ? ceil (ymin * A_HAIR_MORE / y_tick)
  517.        : floor (ymin * A_HAIR_MORE / y_tick));
  518.       while (y_tick_value <= ymax * A_HAIR_MORE)
  519.     {            // draw tick marks on axes
  520.       plot_file.line
  521.         (px (0.), py (fy (y_tick_value)),
  522.          px (-1. * size_of_ticks), py (fy (y_tick_value)));
  523.       plot_file.line
  524.         (px (1.), py (fy (y_tick_value)),
  525.          px (1. + size_of_ticks), py (fy (y_tick_value)));
  526.       SET_TICK_LABEL(y_tick_value);
  527.       plot_file.move
  528.         (px ((y_label_on_right ? 1. : (extended_plot_format ? 0 : -1.
  529.                        * char_width * strlen(tick_label)))
  530.          + (y_label_on_right ? 1. : -1.) * ((size_of_ticks < 0.) ? 0. : size_of_ticks)),
  531.          py (fy (y_tick_value) - .5
  532.          * (extended_plot_format ? 0 : char_height)));
  533.       if (extended_plot_format)
  534.         plot_file.alabel (y_label_on_right ? LEFT_JUSTIFY : RIGHT_JUSTIFY,
  535.                   CENTER_FLUSH, tick_label);
  536.       else
  537.         {
  538.           SET_TICK_LABEL(y_tick_value);
  539.           plot_file.label (tick_label);
  540.         }
  541.       if (grid_style == 2)
  542.         {            // draw grid within box.
  543.           plot_file.linemod ("shortdashed");
  544.           plot_file.line
  545.         (px (0.), py (fy (y_tick_value)),
  546.          px (1.), py (fy (y_tick_value)));
  547.           plot_file.linemod ("solid");
  548.         }
  549.       if ((.5 < fy (y_tick_value + y_tick))
  550.           && y_label.length ())
  551.         {            // put the label between tick marks
  552.           plot_file.move
  553.         (px ((y_label_on_right ? 1. : (extended_plot_format ? 0 : -1.
  554.                            * char_width * strlen (y_label)))
  555.              + (y_label_on_right ? 1. : -1.) * ((size_of_ticks < 0.) ? 0. : size_of_ticks)),
  556.          py (fy (y_tick_value + y_tick / 2.)));
  557.           if (extended_plot_format)
  558.         plot_file.alabel (y_label_on_right ? LEFT_JUSTIFY
  559.                   : RIGHT_JUSTIFY, CENTER_FLUSH, y_label);
  560.           else
  561.         plot_file.label (y_label);
  562.           y_label = "";
  563.         }
  564.       y_tick_value += y_tick;
  565.       if (fabs (y_tick_value / y_tick) < 1e-7) y_tick_value = 0.;
  566.     }
  567.       if (top_label.length ())    // put label above plot.
  568.     {
  569.       plot_file.move
  570.         (px (.5 - (extended_plot_format ? 0 :
  571.                .5 * char_width * top_label.length ())),
  572.          py (1. + size_of_ticks));
  573.       if (extended_plot_format)
  574.         plot_file.alabel (CENTER_JUSTIFY, BOTTOM_FLUSH, top_label);
  575.       else
  576.         plot_file.label (top_label);
  577.     }
  578.     }
  579.   
  580.   if (line_style >= 0)        // set style of lines connecting data points.
  581.     plot_file.linemod (line_style_name[line_style % no_of_line_styles]);
  582.   
  583.                    // draw all the points
  584.   if (point.length () <= 0)
  585.       return 0;            // exit if there is no data.
  586.   i = point.low ();
  587.   int move = 1;        // 1 means move to first point
  588.   double prev_x = point[i].x;
  589.   int clipped = 0;        // 1 means we were outside the box.
  590.   if (line_style >= 0)        // line_style == -1 means omit lines between points
  591.   while (i < point.fence ())
  592.     {                // break line if flag set and x < last x
  593.       if (point[i].x <  prev_x)
  594.     {
  595.       if (switch_style)
  596.         {
  597.         line_style = line_style++;
  598.         plot_file.linemod (line_style_name[line_style % no_of_line_styles]);
  599.         }
  600.       if (break_flag)
  601.         move = 1;
  602.     }
  603.       if (in_box (point[i].x, point[i].y)) // clip out points outside the box
  604.     {
  605.       if (move)
  606.         {
  607.           move = 0;            // only move once for the first point
  608.         plot_file.move (px (fx (point[i].x)), py (fy (point[i].y)));
  609.         }
  610.       else
  611.           if (clipped)
  612.         {
  613.           clipped = 0;
  614.           plot_file.move (px (clip (fx (point[i-1].x))),
  615.                   py (clip (fy (point[i-1].y))));
  616.         plot_file.cont (px (fx (point[i].x)), py (fy (point[i].y)));
  617.     }
  618.           else
  619.         plot_file.cont (px (fx (point[i].x)), py (fy (point[i].y)));
  620.       }
  621.     else
  622.       {
  623.         if (!clipped)
  624.           {
  625.         clipped = 1;
  626.         if (move)
  627.           {
  628.             move = 0;
  629.             plot_file.move (px (clip (fx (point[i].x))),
  630.                     py (clip (fy (point[i].y))));
  631.           }
  632.         else
  633.           plot_file.cont (px (clip (fx (point[i].x))),
  634.                   py (clip (fy (point[i].y))));
  635.           }
  636.       }
  637.     prev_x = point[i].x;
  638.       point.next (i);
  639.     }
  640.                // now draw all the symbols and data labels
  641.   plot_file.linemod ("solid");
  642.   for (i = point.low (); i < point.fence (); point.next (i))
  643.     {
  644.       if (in_box (point[i].x, point[i].y))
  645.     {
  646.       if (point[i].label)
  647.         {
  648.           plot_file.move
  649.         (px (fx (point[i].x)), py (fy (point[i].y)));
  650.           if (extended_plot_format)
  651.         plot_file.alabel (CENTER_JUSTIFY, CENTER_FLUSH, point[i].label);
  652.           else
  653.         plot_file.label (point[i].label);
  654.         }
  655.       else if (default_label.length ())
  656.         {
  657.           plot_file.move
  658.         (px (fx (point[i].x)), py (fy (point[i].y)));
  659.           if (extended_plot_format)
  660.         plot_file.alabel (CENTER_JUSTIFY, CENTER_FLUSH, default_label);
  661.           else
  662.         plot_file.label (default_label);
  663.         }
  664.       if (point[i].symbol >= 0)
  665.         {
  666.           point[i].symbol %= no_of_symbols;
  667.           for (int j=0; (END != symbol[point[i].symbol][j].operation); j++)
  668.         switch (symbol[point[i].symbol][j].operation)
  669.           {
  670.           case CONT:
  671.             plot_file.cont
  672.               (px (fx (point[i].x) 
  673.                + symbol_size * symbol[point[i].symbol][j].x),
  674.                py (fy (point[i].y)
  675.                + symbol_size * symbol[point[i].symbol][j].y));
  676.             break;
  677.           case MOVE:
  678.             plot_file.move
  679.               (px (fx (point[i].x)
  680.                + symbol_size * symbol[point[i].symbol][j].x),
  681.                py (fy (point[i].y)
  682.                + symbol_size * symbol[point[i].symbol][j].y));
  683.             break;
  684.           case CIRCLE:
  685.             plot_file.circle
  686.               (px (fx (point[i].x)), py (fy (point[i].y)),
  687.                (int) (plot_size * width * symbol_size
  688.                   * symbol[point[i].symbol][j].x));
  689.             break;
  690.           case END:
  691.             ;
  692.           }
  693.         }
  694.     }
  695.     }
  696.   return 0;
  697. }
  698.